import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import { isString } from 'lodash-es';
import { ref } from 'vue';
import { appConfig } from '../config/app'
import { Data, Dialog, http, login } from '../index'
import { RefreshTokenExpireError } from '../login'
import { Notification } from '../modal'
import { HttpMethod, HttpRequestConfig, HttpResponseError } from './index'


export const isShowErrorDialog = ref(false)


export interface HttpConfig {
  afterResponse: (json: any, config: HttpRequestConfig, response: AxiosResponse) => any
  beforeRequest: <R>(method: HttpMethod, url: string, params: any, config: any, callback: any) => Promise<R>
  beforeThrowError: (error: HttpResponseError) => Promise<any>
  unauthorizedInterrupt: (content: string, config?: HttpRequestConfig) => Promise<any>
  unauthorizedHandle: (message: string, config?: HttpRequestConfig) => Promise<any>
  checkDate: (serverTime: number) => void
  checkDateOffset: number
  axios: AxiosRequestConfig
  isCache: boolean
  setHeaders: (config: HttpRequestConfig) => void
}

export const httpConfig: HttpConfig = {
  afterResponse(json: any, config: HttpRequestConfig, response: AxiosResponse): any {
    if (response.status === 200) {
      return json
    } else {
      const { message, msg, code, data } = json
      const e = new HttpResponseError(msg ?? message)
      e.code = code
      if (data) {
        e.data = data
      }
      throw e
    }
  },
  beforeRequest<R>(method: HttpMethod, url: string, params: any, config: Data, callback: <R>(method: HttpMethod, url: string, params: any, config: HttpRequestConfig) => Promise<R>): Promise<R> {
    return callback<R>(method, url, params, config)
  },
  async unauthorizedInterrupt(content: string, config?: HttpRequestConfig) {
    await Dialog.error({ title: '未认证', content, okText: '去登录' })
    await login.logout()
  },
  async unauthorizedHandle(content: string, config?: HttpRequestConfig) {
    if (content.includes('过期') || content.includes('expire')) {
      if (config?.refresh) {
        throw new RefreshTokenExpireError()
      }
      if (login.token.isRefreshInProgress()) {
        await login.token.waitForRefresh()
      } else {
        await login.tokenCheck(true, false)
      }
      if (config) {
        delete config.cancelToken
        config.ignoreToken = false
        return await http.request(config)
      }
    } else {
      if (!isShowErrorDialog.value) {
        try {
          isShowErrorDialog.value = true
          await httpConfig.unauthorizedInterrupt(content, config)
        } finally {
          isShowErrorDialog.value = false
        }
      }
    }
  },
  async beforeThrowError({ message, config, code, response }: HttpResponseError): Promise<any> {
    const status = response ? response.status : 0
    const content = isString(message) ? message.replaceAll('【', '「').replaceAll('】', '」') : JSON.stringify(message) ?? ''
    if (status === 401) {
      return await this.unauthorizedHandle(content, config)
    } else if (status === 403 || code === 403) {
      if (!isShowErrorDialog.value) {
        try {
          isShowErrorDialog.value = true
          await Dialog.error({ title: '无权限', content: '对不起，您没有访问接口的权限！' })
        } finally {
          isShowErrorDialog.value = false
        }
      }
    } else if (config) {
      config.ignoreError || Notification.error({ content })
    } else {
      Notification.error({ content })
    }
  },
  checkDateOffset: 10 * 1000,
  checkDate(serverTime: number): void {
    let span = serverTime - Date.now()
    span = Math.round(span / (60 * 1000))
    let content = null
    if (span >= this.checkDateOffset) {
      content = `当前「电脑/手机」比服务器时间慢「${span}」分钟，请调整「电脑/手机」时间。`
    } else if (Math.abs(span) >= this.checkDateOffset) {
      content = `当前「电脑/手机」比服务器时间快「${Math.abs(span)}」分钟，请调整「电脑/手机」时间。`
    }
    content && Notification.error({ id: 'checkDate', title: '时间校验', content, closable: true })
  },
  axios: {
    withCredentials: false,
    timeout: appConfig.env === 'dev' ? 10 * 60 * 1000 : 15 * 1000,
  },
  isCache: window.dmyConfig?.http?.isCache || appConfig.env !== 'dev',
  setHeaders: (config: HttpRequestConfig): void => {
    if (!config.headers) return
    if (appConfig.app.appKeyHeaderName && config.appKey || appConfig.app.key) {
      config.headers[appConfig.app.appKeyHeaderName] = config.appKey || appConfig.app.key
    }
  },
}
